using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using System.Reflection;

namespace FwkLight.CodeExtensions
{
  public class ReflectionHelper
  {
    public static PropertyInfo GetProperty<MODEL, T> ( Expression<Func<MODEL, T>> expression )
    {
      MemberExpression memberExpression = getMemberExpression(expression);

      return (PropertyInfo)memberExpression.Member;
    }

    public static IList<PropertyInfo> GetNonReadonlyProperties<T> ( )
    {
      Type type = typeof(T);
      PropertyInfo[] properties = type.GetProperties();

      IList<PropertyInfo> infos = new List<PropertyInfo>();

      foreach (PropertyInfo property in properties)
      {
        if (!(property.CanRead && property.CanWrite)) continue;
        infos.Add(property);
      }

      return infos;
    }

    private static MemberExpression getMemberExpression<MODEL, T> ( Expression<Func<MODEL, T>> expression )
    {
      return getMemberExpression(expression, true);
    }

    private static MemberExpression getMemberExpression<MODEL, T> ( Expression<Func<MODEL, T>> expression, bool enforceCheck )
    {
      MemberExpression memberExpression = null;
      if (expression.Body.NodeType == ExpressionType.Convert)
      {
        var body = (UnaryExpression)expression.Body;
        memberExpression = body.Operand as MemberExpression;
      }
      else if (expression.Body.NodeType == ExpressionType.MemberAccess)
      {
        memberExpression = expression.Body as MemberExpression;
      }

      if (enforceCheck && memberExpression == null)
      {
        throw new ArgumentException("Not a member access", "member");
      }

      return memberExpression;
    }

    public static string GetPropertyName<MODEL, T> ( Expression<Func<MODEL,T>> expression)
    {
      MemberExpression memberExpression = null;
      if (expression.Body.NodeType == ExpressionType.Convert)
      {
        var body = (UnaryExpression)expression.Body;
        memberExpression = body.Operand as MemberExpression;
      }
      else if (expression.Body.NodeType == ExpressionType.MemberAccess)
      {
        memberExpression = expression.Body as MemberExpression;
      }

      return memberExpression.Member.Name;
    }

    public static T GetPropertyValue<T>(object item, string property)
    {
      return (T)item.GetType().GetProperty(property).GetValue(item, null);
    }
  }
}